from datetime import datetime,timedelta, timezone
from cc.appcoin2.support.feed.py_market_data_client import PyMarketDataClient
from coin.proto.coin_request_pb2 import KlineRequestProto
import coin.feed.fastfeed.feed_pb2 as ff_pb2
from xunkemgmt_client.client.api_client import XunkemgmtClient
import coin2.service.feed.interval_feed_service_pb2 as fs_pb2
import os,sys
from xunkemgmt_client.tool.slack_noti import send_to_slack


def _convert_kline(kline_req, kline_snapshot):
  assert isinstance(kline_snapshot, ff_pb2.KlineSnapshot)
  klines = []
  for kline in kline_snapshot.kline:
    kline_data = {}
    kline_data["open_timestamp"]=kline.open_timestamp
    kline_data["close_timestamp"]=kline.close_timestamp
    kline_data["open"]=kline.open
    kline_data["close"]=kline.close
    kline_data["high"]=kline.high
    kline_data["low"]=kline.low
    kline_data["volume"]=kline.volume
    kline_data["turnover"]=kline.turnover
    kline_data["buy_volume"]=kline.buy_volume
    kline_data["buy_turnover"]=kline.buy_turnover
    klines.append(kline_data)
  klines = [kline for kline in klines if
            kline['open_timestamp'] >= kline_req.start_timestamp and
            kline['open_timestamp'] < kline_req.end_timestamp]
  return klines


def query_from_rest(kline_request, client):
  assert isinstance(kline_request, KlineRequestProto)
  assert isinstance(client, PyMarketDataClient)
  assert kline_request.HasField('start_timestamp')
  assert kline_request.HasField('end_timestamp')
  assert kline_request.end_timestamp >= kline_request.start_timestamp
  kline_request.kline_period = kline_request.kline_period
  raw_data = client.query_klines(kline_request.SerializeToString())
  data =  ff_pb2.Feed()
  data.ParseFromString(raw_data)
  converted = _convert_kline(kline_request, data.kline_snapshot)
  return converted

def query_from_grpc(grpc_query_request):
    with XunkemgmtClient() as client:
      return client.query_interval_feed(grpc_query_request)
    
# https://stackoverflow.com/questions/24277488/in-python-how-to-capture-the-stdout-from-a-c-shared-library-to-a-variable
class DumpStream(object): 
  def __init__(self, stream=sys.stderr):
    self.orig_stream_fileno = stream.fileno()

  def __enter__(self):
    self.r, self.w = os.pipe()
    self.orig_stream_dup = os.dup(self.orig_stream_fileno)
    self.pipe_write = open(self.w, 'w')
    os.dup2(self.w, self.orig_stream_fileno)
    return self.r

  def __exit__(self, type, value, traceback):
    os.close(self.orig_stream_fileno)
    os.dup2(self.orig_stream_dup, self.orig_stream_fileno)
    os.close(self.orig_stream_dup)
    self.pipe_write.close()


def check():
  now_time = datetime.now().replace(tzinfo=timezone.utc)
  start_time = now_time - timedelta(hours=5)
  end_time = now_time - timedelta(hours=3)
  rest_query_client = PyMarketDataClient()
  rest_query_request = KlineRequestProto(
    market_type='Futures',
    exchange='Binance',
    symbol='BTC-USDT.PERPETUAL',
    api_version='v1',
    start_timestamp=int(start_time.timestamp()*1e9),
    end_timestamp=int(end_time.timestamp()*1e9),
    kline_period='KLINE_INTERVAL_1HOUR'
    )
  
  rest_query_client = PyMarketDataClient()

  grpc_query_request = fs_pb2.QueryIntervalFeedRequestProto(
    start_time=int(start_time.timestamp()*1e9),
    end_time=int(end_time.timestamp()*1e9),
    mea='Futures.Binance.v1',
    symbols=["BTC-USDT.PERPETUAL"],
    resolution="1m",
    topics=["volume", "volume_buy", "volume_sell", "open_trade", "close_trade", "high_trade", "low_trade"],
    )
  
  error_msg = ['\nKline Checker Warning:']
  try:
    # test rest query
    with DumpStream() as fd:
      rest_resp = query_from_rest(rest_query_request,rest_query_client)
    with open(fd, 'r') as f:
      stderr_log = f.read()
    lines = stderr_log.split('\n')
    if "Dynamic exception type: std::bad_optional_access" in lines:
      error_msg.append('  rest query binary not up to date')
      print(stderr_log)
    if len(rest_resp) == 0:
      error_msg.append("  rest query return nothing")
    # test grpc query
    grpc_resp = query_from_grpc(grpc_query_request)
    if grpc_resp.status != 'success':
      error_msg.append("  grpc query fail: " + grpc_resp.error_message)
      print(grpc_resp)
  except Exception as e:
    crash_msg = f'  kline query fatal error: {e}'
    error_msg.append(crash_msg)
  finally:
    if len(error_msg) > 1:
      error_msg = "\n".join(error_msg)
      print(error_msg)
      send_to_slack(error_msg,'#coin_feed_noti', 'msg', mention_list=['ziyan','leon','junxiao'])
  
if __name__ == "__main__":
   check()